Skip to content

Add AllocationTrackedMemoryManager and refactor allocators#3120

Merged
JimBobSquarePants merged 13 commits into
mainfrom
js/optimize-allocation-tracking
May 12, 2026
Merged

Add AllocationTrackedMemoryManager and refactor allocators#3120
JimBobSquarePants merged 13 commits into
mainfrom
js/optimize-allocation-tracking

Conversation

@JimBobSquarePants
Copy link
Copy Markdown
Member

@JimBobSquarePants JimBobSquarePants commented Apr 19, 2026

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

An updated implementation of #3056

@antonfirsov your comment in the PR was the key to a better implementation for me.

it might make sense to tweak public API-s to solve this properly

I was so determined not to make a breaking change that I massively overcomplicated the implementation and made it inefficient.

This PR changes things by implementing a new type AllocationTrackedMemoryManager which custom allocator memory managers must implement. By making MemoryAllocator.Allocate non virtual and adding an abstract AllocateCore I can introduce tracking without the allocation and complexity cost.

@JimBobSquarePants JimBobSquarePants added breaking Signifies a binary breaking change. area:allocators labels Apr 19, 2026
@antonfirsov
Copy link
Copy Markdown
Member

antonfirsov commented Apr 20, 2026

It would be very helpful for my review if we could revert #3056, and rebase this on top of the original state. I want to review the feature implementation as a whole thing and now it's difficult to react to specific changes from that PR.

@JimBobSquarePants JimBobSquarePants force-pushed the js/optimize-allocation-tracking branch from 127b5bb to 15fd941 Compare April 22, 2026 12:46
@JimBobSquarePants
Copy link
Copy Markdown
Member Author

@antonfirsov

#3056 has been reverted and this PR rebased over the top. Hopefully this is much easier to review now the diff is clean.

@antonfirsov
Copy link
Copy Markdown
Member

Thanks a lot! I'll come back to this once the Drawing API is concluded.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the MemoryAllocator infrastructure to support accumulative (lifetime) allocation tracking without per-allocation wrapper overhead by introducing a new tracked memory-owner base type (AllocationTrackedMemoryManager<T>) and moving allocator implementations to an AllocateCore pattern.

Changes:

  • Introduces AllocationTrackedMemoryManager<T> + AllocationTrackingState and makes MemoryAllocator.Allocate non-virtual with tracking handled centrally.
  • Updates built-in allocators and memory-owner implementations (managed/unmanaged + memory groups) to participate in the new tracking lifecycle.
  • Extends tests to validate that accumulative allocation reservations are released on owner/group disposal and stabilizes pool-based tests by disabling trimming.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs Updates test allocator to implement AllocateCore and return a tracked memory manager.
tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs Disables pool trimming in a test to avoid timing-related flakiness.
tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs Adjusts allocator factory typing, adds overflow-safe AllocHGlobal, and adds accumulative-limit release tests.
tests/ImageSharp.Tests/Memory/Allocators/SimpleGcMemoryAllocatorTests.cs Adds accumulative-limit release tests for owner and group disposal.
tests/ImageSharp.Tests/Image/ProcessPixelRowsTestBase.cs Updates mock allocator to implement AllocateCore returning tracked owners.
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs Releases allocation tracking when owned groups are disposed; minor indexing cleanup.
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs Adds allocation tracking state to groups and routes group segment allocation through AllocateGroupBuffer.
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs Releases allocation tracking when consumed groups are disposed; minor expression-bodied cleanup.
src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs Adds explicit public modifiers on interface members.
src/ImageSharp/Memory/Allocators/UnmanagedMemoryAllocator.cs Migrates to AllocateCore, adds AllocateGroupBuffer, and exposes AllocateBuffer helper for raw unmanaged owners.
src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs Migrates to AllocateCore and uses raw unmanaged allocation for non-pooled fallback.
src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs Adds options-based constructor and migrates to AllocateCore.
src/ImageSharp/Memory/Allocators/MemoryAllocatorOptions.cs Adds AccumulativeAllocationLimitMegabytes option with validation and docs.
src/ImageSharp/Memory/Allocators/MemoryAllocator.cs Implements centralized accumulative tracking, suppression scope, and AllocateGroupBuffer hook.
src/ImageSharp/Memory/Allocators/Internals/UnmanagedBuffer{T}.cs Switches unmanaged owner to tracked base type and updates dispose path.
src/ImageSharp/Memory/Allocators/Internals/SharedArrayPoolBuffer{T}.cs Updates disposal to DisposeCore and aligns lifetime guard field semantics.
src/ImageSharp/Memory/Allocators/Internals/ManagedBufferBase.cs Switches managed buffer base to the tracked base type.
src/ImageSharp/Memory/Allocators/Internals/BasicArrayBuffer.cs Updates disposal override to DisposeCore.
src/ImageSharp/Memory/Allocators/AllocationOptionsExtensions.cs Removes BOM and adds XML documentation for the Has helper.
src/ImageSharp/Memory/AllocationTrackingState.cs New: state container to release a single reservation exactly once.
src/ImageSharp/Memory/AllocationTrackedMemoryManager{T}.cs New: tracked MemoryManager<T> base that releases reservations on dispose.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/ImageSharp/Memory/Allocators/MemoryAllocatorOptions.cs Outdated
Comment thread src/ImageSharp/Memory/Allocators/MemoryAllocator.cs
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated 7 comments.

Comment thread src/ImageSharp/Memory/Allocators/MemoryAllocator.cs Outdated
Comment thread src/ImageSharp/Memory/Allocators/MemoryAllocator.cs Outdated
Comment thread src/ImageSharp/Memory/Allocators/MemoryAllocator.cs Outdated
Comment thread src/ImageSharp/Memory/Allocators/MemoryAllocator.cs Outdated
Comment thread src/ImageSharp/Memory/AllocationTrackedMemoryManager{T}.cs Outdated
Comment thread tests/ImageSharp.Tests/Image/ProcessPixelRowsTestBase.cs Outdated
Comment thread src/ImageSharp/Memory/Allocators/UnmanagedMemoryAllocator.cs Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/ImageSharp/Memory/Allocators/MemoryAllocatorOptions.cs:45

  • AllocationLimitMegabytes validates its own value, but it does not validate against an already-set AccumulativeAllocationLimitMegabytes. This means setting AccumulativeAllocationLimitMegabytes first and then setting AllocationLimitMegabytes to a larger value can create inconsistent options (the later ApplyOptions will allow single allocations larger than the accumulative limit). Add a symmetric validation in the AllocationLimitMegabytes setter when AccumulativeAllocationLimitMegabytes.HasValue to ensure AllocationLimitMegabytes <= AccumulativeAllocationLimitMegabytes regardless of assignment order.
        {
            if (value.HasValue)
            {
                Guard.MustBeGreaterThan(value.Value, 0, nameof(this.AllocationLimitMegabytes));
            }

Comment thread src/ImageSharp/Memory/Allocators/MemoryAllocatorOptions.cs Outdated
Comment thread src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated 1 comment.

Comment thread src/ImageSharp/Memory/AllocationTrackedMemoryManager{T}.cs Outdated
@JimBobSquarePants JimBobSquarePants merged commit ff36e83 into main May 12, 2026
12 checks passed
@JimBobSquarePants JimBobSquarePants deleted the js/optimize-allocation-tracking branch May 12, 2026 10:54
/// Initializes a new instance of the <see cref="SimpleGcMemoryAllocator"/> class with custom limits.
/// </summary>
/// <param name="options">The <see cref="MemoryAllocatorOptions"/> to apply.</param>
public SimpleGcMemoryAllocator(MemoryAllocatorOptions options) => this.ApplyOptions(options);
Copy link
Copy Markdown
Member

@antonfirsov antonfirsov May 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JimBobSquarePants MemoryAllocatorOptions were not meant to be attached to this type (the feature set is mostly orthogonal). Also, I don't think it brings any value to users to use allocation tracking for anything but the default memory allocator. Scoping the tracking feature as an implementation detail of UniformUnmanagedMemoryPoolMemoryAllocator could simplify things significantly.

Sorry, I was extremely distracted last week because of starting my new job, but I REALLY wanted to come back to this. I can do a deeper post-review tomorrow afternoon.

Copy link
Copy Markdown
Member

@antonfirsov antonfirsov May 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed the release is out now :( Maybe deleting this ctr and AllocationTrackedMemoryManager<T> from the public APIS is not much of a breaking change for a minor update that refactors this. Those things shouldn't really be exposed.

Copy link
Copy Markdown
Member Author

@JimBobSquarePants JimBobSquarePants May 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to ship I’m afraid. No need to apologize at all.

I actually think we should treat SimpleGcMemoryAllocator the same as others here. I'd rather normalize everything through a single channel and ensure that all implementations have tracking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:allocators breaking Signifies a binary breaking change.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants